package com.dongzili.easyes.common.mybatisplus;

import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.core.conditions.AbstractWrapper;
import com.baomidou.mybatisplus.core.toolkit.Assert;
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import com.baomidou.mybatisplus.core.toolkit.LambdaUtils;
import com.baomidou.mybatisplus.core.toolkit.support.ColumnCache;
import com.baomidou.mybatisplus.core.toolkit.support.LambdaMeta;
import com.baomidou.mybatisplus.core.toolkit.support.SFunction;

import java.lang.reflect.Field;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

import com.baomidou.mybatisplus.core.toolkit.support.SerializedLambda;
import org.apache.ibatis.reflection.property.PropertyNamer;
import org.dromara.easyes.annotation.IndexField;

public abstract class PropertyAbstractLambdaWrapper<T, Children extends PropertyAbstractLambdaWrapper<T, Children>> extends AbstractWrapper<T, SFunction<T, ?>, Children> {
    private Map<String, ColumnCache> columnMap = null;
    private boolean initColumnMap = false;
    protected PropertyDbTypeEnum propertyDbTypeEnum = PropertyDbTypeEnum.NONE;

    public PropertyAbstractLambdaWrapper() {
        super();
    }

    @SafeVarargs
    protected final String columnsToString(SFunction<T, ?>... columns) {
        return this.columnsToString(true, columns);
    }

    @SafeVarargs
    protected final String columnsToString(boolean onlyColumn, SFunction<T, ?>... columns) {
        return this.columnsToString(onlyColumn, CollectionUtils.toList(columns));
    }

    protected final String columnsToString(boolean onlyColumn, List<SFunction<T, ?>> columns) {
        return columns.stream().map((i) -> this.columnToString(i, onlyColumn)).collect(Collectors.joining(","));
    }

    protected String columnToString(SFunction<T, ?> column) {
        return this.columnToString(column, true);
    }

    protected String columnToString(SFunction<T, ?> column, boolean onlyColumn) {
        switch (this.propertyDbTypeEnum){
            case ES:
                return this.getIndexFieldValue(column);
            case MYSQL:
                return this.getTableFieldValue(column);
            default:
                return this.getPropertyName(column);
        }
    }

    private String getIndexFieldValue(SFunction<T, ?> column){
        try {
            LambdaMeta lambda = LambdaUtils.extract(column);
            Class<?> entityClass = lambda.getInstantiatedClass();

            String fieldName = getPropertyName(column);
            // 反射获取字段上的@TableField注解
            Field field = entityClass.getDeclaredField(fieldName);
            IndexField tableField = field.getAnnotation(IndexField.class);

            // 若注解存在且value不为空，则使用注解值
            if (tableField != null && !tableField.value().isEmpty() && tableField.exist()) {
                return tableField.value();
            }
            return fieldName;
        } catch (Exception e) {
            // 异常时回退到原有逻辑
            return getPropertyName(column);
        }
    }
    private String getTableFieldValue(SFunction<T, ?> column){
        try {
            // 解析Lambda获取字段信息
            LambdaMeta lambda = LambdaUtils.extract(column);
            Class<?> entityClass = lambda.getInstantiatedClass();

//            Class<?> entityClass = Class.forName(methodType);
            String fieldName = getPropertyName(column);
            // 反射获取字段上的@TableField注解
            Field field = entityClass.getDeclaredField(fieldName);
            TableField tableField = field.getAnnotation(TableField.class);

            // 若注解存在且value不为空，则使用注解值
            if (tableField != null && !tableField.value().isEmpty() && tableField.exist()) {
                return tableField.value();
            }
            return fieldName;
        } catch (Exception e) {
            // 异常时回退到原有逻辑
            return getPropertyName(column);
        }
    }
    private String getPropertyName(SFunction<T, ?> column) {
        try {
            SerializedLambda lambda = SerializedLambda.extract(column);
            String methodName = lambda.getImplMethodName();

            String propertyName;
            if (methodName.startsWith("get")) {
                propertyName = methodName.substring(3);
            } else if (methodName.startsWith("is")) {
                propertyName = methodName.substring(2);
            } else {
                propertyName = methodName;
            }

            if (!propertyName.isEmpty()) {
                propertyName = Character.toLowerCase(propertyName.charAt(0)) + propertyName.substring(1);
            }

            return propertyName;
        } catch (Exception e) {
            // 如果获取属性名失败，回退到默认行为
            return super.columnToString(column);
        }
    }

    @SafeVarargs
    public final Children groupBy(boolean condition, SFunction<T, ?> column, SFunction<T, ?>... columns) {
        return super.groupBy(condition, column, columns);
    }

    @SafeVarargs
    public final Children orderBy(boolean condition, boolean isAsc, SFunction<T, ?> column, SFunction<T, ?>... columns) {
        return super.orderBy(condition, isAsc, column, columns);
    }

    @SafeVarargs
    public final Children groupBy(SFunction<T, ?> column, SFunction<T, ?>... columns) {
        return this.doGroupBy(true, column, CollectionUtils.toList(columns));
    }

    public Children groupBy(boolean condition, SFunction<T, ?> column, List<SFunction<T, ?>> columns) {
        return this.doGroupBy(condition, column, columns);
    }

    @SafeVarargs
    public final Children orderByAsc(SFunction<T, ?> column, SFunction<T, ?>... columns) {
        return super.orderByAsc(column, columns);
    }

    @SafeVarargs
    public final Children orderByAsc(boolean condition, SFunction<T, ?> column, SFunction<T, ?>... columns) {
        return super.orderByAsc(condition, column, columns);
    }

    @SafeVarargs
    public final Children orderByDesc(SFunction<T, ?> column, SFunction<T, ?>... columns) {
        return super.orderByDesc(column, columns);
    }

    @SafeVarargs
    public final Children orderByDesc(boolean condition, SFunction<T, ?> column, SFunction<T, ?>... columns) {
        return super.orderByDesc(condition, column, columns);
    }

    protected ColumnCache getColumnCache(SFunction<T, ?> column) {
        LambdaMeta meta = LambdaUtils.extract(column);
        String fieldName = PropertyNamer.methodToProperty(meta.getImplMethodName());
        Class<?> instantiatedClass = meta.getInstantiatedClass();
        this.tryInitCache(instantiatedClass);
        return this.getColumnCache(fieldName, instantiatedClass);
    }

    private void tryInitCache(Class<?> lambdaClass) {
        if (!this.initColumnMap) {
            Class<T> entityClass = this.getEntityClass();
            if (entityClass != null) {
                lambdaClass = entityClass;
            }

            this.columnMap = LambdaUtils.getColumnMap(lambdaClass);
            Assert.notNull(this.columnMap, "can not find lambda cache for this entity [%s]", new Object[]{lambdaClass.getName()});
            this.initColumnMap = true;
        }

    }

    private ColumnCache getColumnCache(String fieldName, Class<?> lambdaClass) {
        ColumnCache columnCache = (ColumnCache)this.columnMap.get(LambdaUtils.formatKey(fieldName));
        Assert.notNull(columnCache, "can not find lambda cache for this property [%s] of entity [%s]", new Object[]{fieldName, lambdaClass.getName()});
        return columnCache;
    }
}